/* ****************************************************************** **
**    OpenSees - Open System for Earthquake Engineering Simulation    **
**          Pacific Earthquake Engineering Research Center            **
**                                                                    **
**                                                                    **
** (C) Copyright 1999, The Regents of the University of California    **
** All Rights Reserved.                                               **
**                                                                    **
** Commercial use of this program without express permission of the   **
** University of California, Berkeley, is strictly prohibited.  See   **
** file 'COPYRIGHT'  in main directory for information on usage and   **
** redistribution,  and for a DISCLAIMER OF ALL WARRANTIES.           **
**                                                                    **
** Developed by:                                                      **
**   Frank McKenna (fmckenna@ce.berkeley.edu)                         **
**   Gregory L. Fenves (fenves@ce.berkeley.edu)                       **
**   Filip C. Filippou (filippou@ce.berkeley.edu)                     **
**                                                                    **
** ****************************************************************** */

// Written: User-defined implementation for UDP Recorder
// Purpose: This file contains the implementation for UDP_Recorder.

#include <UDP_Recorder.h>
#include <Domain.h>
#include <Node.h>
#include <Channel.h>
#include <FEM_ObjectBroker.h>
#include <classTags.h>
#include <OPS_Globals.h>
#include <iostream>
#include <cstring>
#include <stdio.h>
#include <actor/message/Message.h>

UDP_Recorder::UDP_Recorder(int nodeTag, int dof, const char* serverIP, int port)
    : Recorder(RECORDER_TAGS_UDP_Recorder),
      theNodeTag(nodeTag), theDOF(dof), serverPort(port),
      theDomain(0), theNode(0), socketInitialized(false)
{
    serverAddress = new char[strlen(serverIP) + 1];
    strcpy(serverAddress, serverIP);
    
#ifdef _WIN32
    sockfd = INVALID_SOCKET;
    ZeroMemory(&wsaData, sizeof(wsaData));
#else
    sockfd = -1;
#endif
    
    memset(&servaddr, 0, sizeof(servaddr));
    
    if (initializeSocket() != 0) {
        opserr << "UDP_Recorder::UDP_Recorder - failed to initialize UDP socket\n";
    }
}

UDP_Recorder::~UDP_Recorder()
{
    cleanupSocket();
    if (serverAddress != 0) {
        delete[] serverAddress;
    }
}

int UDP_Recorder::initializeSocket()
{
#ifdef _WIN32
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        opserr << "UDP_Recorder - WSAStartup failed\n";
        return -1;
    }
    
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == INVALID_SOCKET) {
        opserr << "UDP_Recorder - socket creation failed\n";
        WSACleanup();
        return -1;
    }
#else
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        opserr << "UDP_Recorder - socket creation failed\n";
        return -1;
    }
#endif
    
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(serverPort);
    
#ifdef _WIN32
    servaddr.sin_addr.s_addr = inet_addr(serverAddress);
    if (servaddr.sin_addr.s_addr == INADDR_NONE) {
        opserr << "UDP_Recorder - invalid server address: " << serverAddress << "\n";
        cleanupSocket();
        return -1;
    }
#else
    if (inet_pton(AF_INET, serverAddress, &servaddr.sin_addr) <= 0) {
        opserr << "UDP_Recorder - invalid server address: " << serverAddress << "\n";
        cleanupSocket();
        return -1;
    }
#endif
    
    socketInitialized = true;
    opserr << "UDP_Recorder - initialized to send to " << serverAddress << ":" << serverPort << "\n";
    return 0;
}

void UDP_Recorder::cleanupSocket()
{
    if (socketInitialized) {
#ifdef _WIN32
        if (sockfd != INVALID_SOCKET) {
            closesocket(sockfd);
            sockfd = INVALID_SOCKET;
        }
        WSACleanup();
#else
        if (sockfd >= 0) {
            close(sockfd);
            sockfd = -1;
        }
#endif
        socketInitialized = false;
    }
}

// 修改sendData方法以发送二进制数据而非字符串
int UDP_Recorder::sendData(double value)
{
	if (!socketInitialized) {
		return -1;
	}

	// 直接发送double值的二进制表示
#ifdef _WIN32
	int sentBytes = sendto(sockfd, (const char*)& value, sizeof(double), 0,
		(const struct sockaddr*) & servaddr, sizeof(servaddr));
#else
	ssize_t sentBytes = sendto(sockfd, (const char*)& value, sizeof(double), 0,
		(const struct sockaddr*) & servaddr, sizeof(servaddr));
#endif

	if (sentBytes < 0) {
		opserr << "UDP_Recorder - failed to send data\n";
		return -1;
	}

	return 0;
}

int UDP_Recorder::record(int commitTag, double timeStamp)
{
    if (theDomain == 0 || theNode == 0) {
        return -1;
    }
    
    const Vector& disp = theNode->getDisp();
    
    if (theDOF < 0 || theDOF >= disp.Size()) {
        opserr << "UDP_Recorder::record - invalid DOF " << theDOF << " for node " << theNodeTag << "\n";
        return -1;
    }
    
    double displacement = disp(theDOF);
    
    if (sendData(displacement) != 0) {
        opserr << "UDP_Recorder::record - failed to send displacement data\n";
        return -1;
    }
    
    return 0;
}

int UDP_Recorder::flush()
{
    return 0;
}

int UDP_Recorder::restart(void)
{
    return 0;
}

int UDP_Recorder::domainChanged(void)
{
    if (theDomain == 0) {
        return -1;
    }
    
    theNode = theDomain->getNode(theNodeTag);
    if (theNode == 0) {
        opserr << "UDP_Recorder::domainChanged - node " << theNodeTag << " not found\n";
        return -1;
    }
    
    return 0;
}

int UDP_Recorder::setDomain(Domain &domain)
{
    theDomain = &domain;
    return domainChanged();
}

int UDP_Recorder::sendSelf(int commitTag, Channel &theChannel)
{
    int res = 0;
    
    static Vector data(4);
    data(0) = (double)theNodeTag;
    data(1) = (double)theDOF;
    data(2) = (double)serverPort;
    data(3) = (double)strlen(serverAddress);
    
    res = theChannel.sendVector(this->getDbTag(), commitTag, data);
    if (res < 0) {
        opserr << "UDP_Recorder::sendSelf - failed to send data\n";
        return res;
    }
    
    // 使用Message类发送字符串数据
    if (serverAddress != 0 && strlen(serverAddress) > 0) {
        Message theMessage(serverAddress, strlen(serverAddress));
        res = theChannel.sendMsg(this->getDbTag(), commitTag, theMessage);
        if (res < 0) {
            opserr << "UDP_Recorder::sendSelf - failed to send address\n";
            return res;
        }
    }
    
    return res;
}

int UDP_Recorder::recvSelf(int commitTag, Channel &theChannel, 
                          FEM_ObjectBroker &theBroker)
{
    int res = 0;
    
    static Vector data(4);
    res = theChannel.recvVector(this->getDbTag(), commitTag, data);
    if (res < 0) {
        opserr << "UDP_Recorder::recvSelf - failed to receive data\n";
        return res;
    }
    
    theNodeTag = (int)data(0);
    theDOF = (int)data(1);
    serverPort = (int)data(2);
    int addrLen = (int)data(3);
    
    // 使用Message类接收字符串数据
    if (addrLen > 0) {
        if (serverAddress != 0) {
            delete[] serverAddress;
        }
        
        Message theMessage;
        char* tempData = new char[addrLen+1];
        theMessage.setData(tempData, addrLen);
        
        res = theChannel.recvMsg(this->getDbTag(), commitTag, theMessage);
        if (res < 0) {
            delete[] tempData;
            opserr << "UDP_Recorder::recvSelf - failed to receive address\n";
            return res;
        }
        
        serverAddress = new char[addrLen + 1];
        memcpy(serverAddress, theMessage.getData(), addrLen);
        serverAddress[addrLen] = '\0';
        
        delete[] tempData;
    }
    
    if (initializeSocket() != 0) {
        opserr << "UDP_Recorder::recvSelf - failed to initialize socket\n";
        return -1;
    }
    
    return res;
}

void UDP_Recorder::Print(OPS_Stream &s, int flag)
{
    s << "UDP_Recorder: \n";
    s << "  Node: " << theNodeTag << ", DOF: " << theDOF << "\n";
    s << "  Server: " << serverAddress << ":" << serverPort << "\n";
}